﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Security.Permissions;
using System.Text;
using System.Web;
using Org.BouncyCastle.Cms;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Security.Certificates;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.X509;
using EntGuayasSLA.BL.Archivos.Io;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate;

namespace EntGuayasSLA.BL.Archivos.Pdf
{
    public static class Certificate
    {
        public static X509Certificate2 GetX509Certificate2(string password, string fileName)
        {
          
            X509Certificate2 newCert = new X509Certificate2(fileName, password, X509KeyStorageFlags.MachineKeySet);
            return newCert;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="certificate"></param>
        /// <param name="pthFile">extension private.pem</param>
        public static void CreatePrivateKeyPEM(X509Certificate2 certificate,string pthFile)
        {
            // Now you have your private key in binary form as you wanted
            // You can use rsa.ExportParameters() or rsa.ExportCspBlob() to get you bytes
            // depending on format you need them in
            RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)certificate.PrivateKey;
            
            // Just for lulz, let's write out the PEM representation of the private key
            // using Bouncy Castle, so that we are 100% sure that the result is exaclty the same as:
            // openssl pkcs12 -in filename.pfx -nocerts -out privateKey.pem
            // openssl.exe rsa -in privateKey.pem -out private.pem

            // You should of course dispose of / close the streams properly. I'm skipping this part for brevity
            MemoryStream memoryStream = new MemoryStream();
            TextWriter streamWriter = new StreamWriter(memoryStream);
            PemWriter pemWriter = new PemWriter(streamWriter);
            

            AsymmetricCipherKeyPair keyPair = DotNetUtilities.GetRsaKeyPair(rsa);
            pemWriter.WriteObject(keyPair.Private);
            //keyPair.Public
            streamWriter.Flush();

            // Here is the output with ---BEGIN RSA PRIVATE KEY---
            // that should be exactly the same as in private.pem

            string sbuffer=Encoding.ASCII.GetString(memoryStream.GetBuffer());
            using (FileStream file = new FileStream(pthFile, FileMode.Create, FileAccess.Write))
            {
                memoryStream.WriteTo(file);
            }
            memoryStream.Close();
            memoryStream.Dispose();

        }
        public static CmsPbeKey GetPrivateKey(string pthFile)
        {
            var fileStream = System.IO.File.OpenText(pthFile);
            var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(fileStream);
            var objReader = pemReader.ReadObject();
            if (objReader == null)
                throw new Exception("Didn't find OpenSSL key");

            CmsPbeKey KeyPair = (CmsPbeKey)objReader;
            ICipherParameters icmprm = (ICipherParameters)objReader;

          
            return ( KeyPair );
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="signatureCert"></param>
        /// <returns></returns>
        public static X509Certificate GetX509Certificate(X509Certificate2 signatureCert)
        {
            var parser = new X509CertificateParser();
            var _x509cert = parser.ReadCertificate(signatureCert.GetRawCertData());
            return _x509cert;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="objCert"></param>
        /// <param name="msgInfo"></param>
        /// <returns></returns>
        public static Boolean VerifyCertificate(X509Certificate2 objCert, out string msgInfo)
        {
            X509Chain objChain = new X509Chain();
            Boolean flag = false;

            msgInfo = "";
            //Verifico toda la cadena de revocación
            objChain.ChainPolicy.RevocationFlag = X509RevocationFlag.EntireChain;
            objChain.ChainPolicy.RevocationMode = X509RevocationMode.Online;//    X509RevocationMode.Online | X509RevocationMode.Offline;
          
            //Timeout para las listas de revocación
            objChain.ChainPolicy.UrlRetrievalTimeout = new TimeSpan(0, 0, 30);

            //Verificar todo
            objChain.ChainPolicy.VerificationFlags = X509VerificationFlags.NoFlag;

            //Se puede cambiar la fecha de verificación
            //objChain.ChainPolicy.VerificationTime = new DateTime(1999, 1, 1);

            objChain.Build(objCert);
            flag = (objChain.ChainStatus.Length != 0);
            if (flag)
                foreach (X509ChainStatus objChainStatus in objChain.ChainStatus)
                    msgInfo = (objChainStatus.Status.ToString() + " - " + objChainStatus.StatusInformation);
            else
                msgInfo = "Ok";
            return (!flag);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="cert"></param>
        public static Boolean VerifyCertificateDate(X509Certificate2 objCert, out  StringBuilder objSB)
        {
           //objCert.NotBefore objCert.NotAfter
            Boolean flag = false;

            DateTime dt1 = new DateTime();
            dt1 = Convert.ToDateTime(objCert.NotBefore);
            DateTime dt2 = new DateTime();
            dt2 = Convert.ToDateTime(objCert.NotAfter);
            objSB = new StringBuilder("");

            if (DateTime.Now.Date >= dt1.Date && DateTime.Now.Date <=dt2)
            {

                flag = true;
                objSB.AppendLine("Ok");
            }
            else
            {


                objSB.AppendLine("Válido desde = " + objCert.NotBefore.ToString("yyyy-MM-dd HH:mm:ss.ff"));
                objSB.AppendLine("Válido hasta = " + objCert.NotAfter.ToString("yyyy-MM-dd HH:mm:ss.ff"));
            }
            return (flag);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="objCert"></param>
        /// <param name="signDate"></param>
        /// <returns></returns>
        public static StringBuilder ShowCertificateInfo(X509Certificate2 objCert, DateTime signDate)
        {
            StringBuilder objSB = new StringBuilder("");

            //Detalle
            objSB.AppendLine("<p><b>Certificado </b>" + objCert.Subject+"</p>");
            objSB.AppendLine("<p><b>Emisor </b> " + objCert.Issuer + "</p>");
            objSB.AppendLine("<p><b>Válido desde </b>" + objCert.NotBefore.ToString() + "</p>");
            objSB.AppendLine("<p><b>Válido hasta </b>" + objCert.NotAfter.ToString() + "</p>");
            objSB.AppendLine("<p><b>Tamaño de la clave </b> " + objCert.PublicKey.Key.KeySize.ToString() + "</p>");
            objSB.AppendLine("<p><b>Número de serie </b> " + objCert.SerialNumber + "</p>");
            objSB.AppendLine("<p><b>Hash </b> " + objCert.Thumbprint + "</p>");

            //Extensiones
            /*
            objSB.AppendLine("\nExtensiones:\n");
            foreach (X509Extension objExt in objCert.Extensions)
            {
                objSB.AppendLine(objExt.Oid.FriendlyName + " (" + objExt.Oid.Value + ')');

                if (objExt.Oid.FriendlyName == "Key Usage")
                {
                    X509KeyUsageExtension ext = (X509KeyUsageExtension)objExt;
                    objSB.AppendLine("    " + ext.KeyUsages);
                }

                if (objExt.Oid.FriendlyName == "Basic Constraints")
                {
                    X509BasicConstraintsExtension ext = (X509BasicConstraintsExtension)objExt;
                    objSB.AppendLine("    " + ext.CertificateAuthority);
                    objSB.AppendLine("    " + ext.HasPathLengthConstraint);
                    objSB.AppendLine("    " + ext.PathLengthConstraint);
                }

                if (objExt.Oid.FriendlyName == "Subject Key Identifier")
                {
                    X509SubjectKeyIdentifierExtension ext = (X509SubjectKeyIdentifierExtension)objExt;
                    objSB.AppendLine("    " + ext.SubjectKeyIdentifier);
                }

                if (objExt.Oid.FriendlyName == "Enhanced Key Usage") //2.5.29.37
                {
                    X509EnhancedKeyUsageExtension ext = (X509EnhancedKeyUsageExtension)objExt;
                    OidCollection objOids = ext.EnhancedKeyUsages;
                    foreach (Oid oid in objOids)
                        objSB.AppendLine("    " + oid.FriendlyName + " (" + oid.Value + ')');
                }
            }*/
            return objSB;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="cert"></param>
        /// <param name="signDate"></param>
        /// <returns></returns>
        public static StringBuilder ShowCertificateInfo(Org.BouncyCastle.X509.X509Certificate cert, DateTime signDate)
        {
            StringBuilder objSB = new StringBuilder("Detalle del certificado: \n\n");
            objSB.AppendLine("Issuer: " + cert.IssuerDN);
            objSB.AppendLine("Subject: " + cert.SubjectDN);
            objSB.AppendLine("Valid from: " + cert.NotBefore.ToString("yyyy-MM-dd HH:mm:ss.ff"));
            objSB.AppendLine("Valid to: " + cert.NotAfter.ToString("yyyy-MM-dd HH:mm:ss.ff"));
            try
            {
                cert.CheckValidity(signDate);
                objSB.AppendLine("The certificate was valid at the time of signing.");
            }
            catch (CertificateExpiredException e)
            {
                objSB.AppendLine("The certificate was expired at the time of signing.");
            }
            catch (CertificateNotYetValidException e)
            {
                objSB.AppendLine("The certificate wasn't valid yet at the time of signing.");
            }
            try
            {
                cert.CheckValidity();
                objSB.AppendLine("The certificate is still valid.");
            }
            catch (CertificateExpiredException e)
            {
                objSB.AppendLine("The certificate has expired.");
            }
            catch (CertificateNotYetValidException e)
            {
                objSB.AppendLine("The certificate isn't valid yet.");
            }

            return (objSB);

        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="CerName"></param>
        public static void FindCertificate(string CerName)
        {
            //"CN=mypc.domainname"
            //
           
            var certificateStore = new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine);
            certificateStore.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
            var certificateCollection = certificateStore.Certificates.Find(X509FindType.FindBySubjectName,
                                                                           CerName, false);

            certificateStore.Close();

            var certificate = certificateCollection[0];
        }
        /// <summary>
        /// _certificate = FindCertificate( StoreLocation.LocalMachine, StoreName.My, X509FindType.FindByThumbprint, thumbPrint );
        /// </summary>
        /// <param name="location"></param>
        /// <param name="name"></param>
        /// <param name="findType"></param>
        /// <param name="findValue"></param>
        /// <returns></returns>
        private static X509Certificate2 FindCertificate( StoreName name,StoreLocation location, X509FindType findType, string findValue)
        {
            //X509Store(StoreName.Root, StoreLocation.LocalMachine);
            X509Store store = new X509Store(name, location);
            try
            {
                // create and open store for read-only access
                store.Open(OpenFlags.ReadOnly);

                // search store
                X509Certificate2Collection col = store.Certificates.Find(findType, findValue, true);

                if (col.Count != 0)
                    // return first certificate found
                    return col[0];
                else
                    return null;
            }
            // always close the store
            finally { store.Close(); }
        }

        [StorePermission(SecurityAction.Demand, Flags = StorePermissionFlags.AddToStore)]
        //public static void InstallCertificate(string fileName, string pwd,string serial)
        public static void InstallCertificate(X509Certificate2 certificate, string serial)
        {

            //--var certificate = new System.Security.Cryptography.X509Certificates.X509Certificate2(fileName, pwd);
            
            //X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            //StorePermission sp = new StorePermission(PermissionState.Unrestricted);
            //sp.Flags = StorePermissionFlags.OpenStore;
            //sp.Assert();

            var _cerrootstore = FindCertificate( StoreName.Root,StoreLocation.LocalMachine, X509FindType.FindBySerialNumber, serial);
            var _cerpublisher = FindCertificate( StoreName.TrustedPublisher,StoreLocation.LocalMachine, X509FindType.FindBySerialNumber, serial);
            var _cercertathor = FindCertificate( StoreName.AuthRoot,StoreLocation.LocalMachine, X509FindType.FindBySerialNumber, serial);
           
            X509Store trustedrootstore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
            X509Store trustedpublisherstore = new X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine);
            X509Store trustedrootcertatho = new X509Store(StoreName.AuthRoot, StoreLocation.LocalMachine);

            try
            {

                if (_cerrootstore==null)
                {
                    trustedrootstore.Open(OpenFlags.MaxAllowed);//
                    trustedrootstore.Add(certificate);
                }

                if (_cerrootstore == null)
                {
                    trustedpublisherstore.Open(OpenFlags.MaxAllowed);//
                    trustedpublisherstore.Add(certificate);

                }
               
                if (_cerrootstore == null)
                {
                    trustedrootcertatho.Open(OpenFlags.ReadWrite);
                    trustedrootcertatho.Add(certificate);

                }
                
            }
            catch (SecurityException e)
            {
                string slog=("Security exception thrown when attempting: " +
                    ((StorePermission)e.FirstPermissionThatFailed).Flags);
                return;
            }
            finally
            {
                trustedrootstore.Close();
                trustedpublisherstore.Close();
                trustedrootcertatho.Close();
            }



        }
       
        /// <summary>
        /// 
        /// </summary>
        static void ImportCert()
        {
            X509Certificate2 cert = new X509Certificate2("alice.cer");
            X509Store store = new X509Store("Test", StoreLocation.CurrentUser);
            try
            {
                store.Open(OpenFlags.ReadWrite);
                store.Add(cert);
            }
            finally { store.Close(); }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="location"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        private static X509Certificate2 PickCertificate(
            StoreLocation location, StoreName name)
        {
            X509Store store = new X509Store(name, location);
            try
            {
                store.Open(OpenFlags.ReadOnly);

                // pick a certificate from the store
                X509Certificate2 cert =
                    X509Certificate2UI.SelectFromCollection(
                        store.Certificates, "Caption",
                        "Message", X509SelectionFlag.SingleSelection)[0];

                // show certificate details dialog
                X509Certificate2UI.DisplayCertificate(cert);
                return cert;
            }
            finally { store.Close(); }
        }

        public static void DeleteCertificate(string findValue)
        {
            X509Store trustedrootstore = new X509Store(StoreName.Root, StoreLocation.LocalMachine);
            X509Store trustedpublisherstore = new X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine);
            X509Store trustedrootcertatho = new X509Store(StoreName.AuthRoot, StoreLocation.LocalMachine);
            DeleteCertificate(X509FindType.FindBySerialNumber, findValue, trustedrootstore);
            DeleteCertificate(X509FindType.FindBySerialNumber, findValue, trustedpublisherstore);
            DeleteCertificate(X509FindType.FindBySerialNumber, findValue, trustedrootcertatho);
        }
        
        /// <summary>
        /// Borrar todos los certificados con un nombre  
        /// </summary>
        /// <param name="strCertificateName"></param>
        /// <param name="objStore"></param>
        private static void DeleteCertificate(X509FindType findType, string findValue, X509Store objStore)
        {
            X509Certificate2Collection objColCertificates;

            // Abre el almacén
            objStore.Open(OpenFlags.ReadWrite);
            // Localiza los certificados
            objColCertificates = objStore.Certificates.Find(findType, findValue, true);
            // Elimina los certificados localizados
            if (objColCertificates != null && objColCertificates.Count > 0)
                objStore.RemoveRange(objColCertificates);
            // Cierra el almacén
            objStore.Close();
        }

        /// <summary>
        /// Borrar únicamente el que tenga determinada firma 
        /// </summary>
        /// <param name="strCertificateName"></param>
        /// <param name="strThumbPrint"></param>
        /// <param name="objStore"></param>
        public static void DeleteCertificate(string strCertificateName, string strThumbPrint, X509Store objStore)
        {
            X509Certificate2Collection objColCertificates;

            // Abre el almacén
            objStore.Open(OpenFlags.ReadWrite);
            // Obtiene los certificados
            objColCertificates = objStore.Certificates.Find(X509FindType.FindBySubjectName, strCertificateName, true);
            // Elimina el certificado adecuado
            if (objColCertificates != null && objColCertificates.Count > 0)
            {
                bool blnDeleted = false;

                foreach (X509Certificate2 objCertificate in objColCertificates)
                    if (!blnDeleted && objCertificate.Thumbprint.Equals(strThumbPrint, StringComparison.CurrentCultureIgnoreCase))
                    { // Elimina el certificado	
                        objStore.Remove(objCertificate);
                        // Indica que se ha borrado
                        blnDeleted = true;
                    }
            }
            // Cierra el almacén
            objStore.Close();
        }
        /// <summary>
        /// Obtiene los certificados de un almacén
        /// </summary>
        /// <param name="objStore"></param>
        /// <returns></returns>
        public static X509Certificate2Collection GetCertificates(X509Store objStore)
        {
            X509Certificate2Collection objColCertificates = null;

            // Abre el almacén para lectura
            objStore.Open(OpenFlags.ReadOnly);
            // Obtiene los certificados
            objColCertificates = objStore.Certificates;
            // Cierra el almacén
            objStore.Close();
            // Devuelve la colección de certificados
            return objColCertificates;
        }
        /// <summary>
        /// Crea un almacén
        /// </summary>
        /// <param name="strName"></param>
        /// <param name="intLocation"></param>
        public static void CreateStore(string strName, StoreLocation intLocation)
        {
            X509Store objStore = new X509Store(strName, intLocation);

            // Abre el almacén para escritura
            objStore.Open(OpenFlags.ReadWrite);
            // Cierra el almacén
            objStore.Close();
        }
        /// <summary>
        /// Obtiene un almacén
        /// </summary>
        /// <param name="strName"></param>
        /// <param name="intStoreLocation"></param>
        /// <returns></returns>
        public static X509Store GetStore(string strName, StoreLocation intStoreLocation)
        {
            return new X509Store(strName, intStoreLocation);
        }
        /// <summary>
        /// Obtiene un almacén
        /// </summary>
        /// <param name="intStoreName"></param>
        /// <param name="intStoreLocation"></param>
        /// <returns></returns>
        public static X509Store GetStore(StoreName intStoreName, StoreLocation intStoreLocation)
        {
            return new X509Store(intStoreName, intStoreLocation);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="CERT_PASSW"></param>
        /// <param name="fileFullPath">@"C:/Users/USUARIO/Desktop/py/iTextSharp/iPdfTextSharp/Resource/Cer/cerRoot.pfx"</param>
        /// <returns></returns>
        public static void ReadAlias(string CERT_PASSW,string fileFullPathPfx )
        {

            ICipherParameters parameters = null;
            //Read certificate 
            using (StreamReader reader = new StreamReader(fileFullPathPfx))
            {
                Pkcs12Store store = new Pkcs12Store(reader.BaseStream, CERT_PASSW.ToCharArray());
                foreach (string n in store.Aliases)
                {
                    if (store.IsKeyEntry(n))
                    {
                        AsymmetricKeyEntry key = store.GetKey(n);
                        parameters = store.GetKey(n).Key;
                        if (key.Key.IsPrivate)
                        {
                            RsaPrivateCrtKeyParameters parametersRsa = key.Key as RsaPrivateCrtKeyParameters;
                            Console.WriteLine(parametersRsa.PublicExponent);
                        }
                    }
                }
            }
        }
        //

    }
}